﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using GameBase;


namespace MilitaryPluginInterface
{
    public abstract class Military : GameObject, MilitaryPluginInterface.IMilitary
    {
        public enum SOLDIER_STATE
        {
            Idle,
            Walk,
            Attack,
            Die
        }
        protected SOLDIER_STATE _currentInfantryState = SOLDIER_STATE.Idle;

        protected LoopSprite _idle;
        protected LoopSprite _walk;
        protected LoopSprite _attack;
        protected OneTimeSprite _die;
        protected LoopSprite _bullet;

        protected MilitaryRank _rank;

        protected int _rangeAttack;
        protected int _strengthAttack;
        protected int _defend;
        protected Vector2 _targetPosition;
        protected Vector2 _finalTarget;
        protected float _speed;
        protected int _produceTime;
        protected int _delay;
        protected string _imgStr;
        protected int _kill = 0;
        protected bool _isAutoControl = true;
        protected Vector2 _totalBeForced = Vector2.Zero;
        protected bool _isTargetPerson = false;
        protected bool _isChosen = false;
        protected Military _militaryForAttack = null;

        protected AIBrain _AI;
        protected MilitaryState _state;

        #region Propertises
        public Military MilitaryForAttack
        {
            get { return _militaryForAttack; }
            set { _militaryForAttack = value; }
        }

        public bool IsAutoControl
        {
            get { return _isAutoControl; }
            set { _isAutoControl = value; }
        }

        public MilitaryState State
        {
            get { return _state; }
            set { _state = value; }
        }

        public override float Scale
        {
            get { return _scale; }
            set
            {
                _scale = value;
                _idle.Scale = _scale;
                _attack.Scale = _scale;
                _walk.Scale = _scale;
            }
        }

        protected Vector2 TotalBeForced
        {
            get { return _totalBeForced; }
            set 
            {
                if (value.Length() <= GameBase.Constant.maxForce)
                    _totalBeForced = value;
                else
                {
                    _totalBeForced.Normalize();
                    _totalBeForced *= GameBase.Constant.maxForce;
                }
            }
        }

        public SOLDIER_STATE CurrentState
        {
            get { return _currentInfantryState; }
            set 
            { 
                _currentInfantryState = value;
                switch (_currentInfantryState)
                {
                    case SOLDIER_STATE.Attack:
                        _currentState = _attack;
                        break;
                    case SOLDIER_STATE.Idle:
                        _currentState = _idle;
                        break;
                    case SOLDIER_STATE.Walk:
                        _currentState = _walk;
                        break;
                    case SOLDIER_STATE.Die:
                        _currentState = _die;
                        break;
                }
                Size = _currentState.Size;
            }
        }

        public Vector2 FinalTarget
        {
            get { return _finalTarget; }
            set { _finalTarget = value; }
        }

        public LoopSprite Bullet
        {
            get { return _bullet; }
            set { _bullet = value; }
        }
                
        public float Speed
        {
            get { return _speed; }
            set { _speed = value; }
        }
        public Vector2 TargetPosition
        {
            get { return _targetPosition; }
            set { _targetPosition = value; }
        }

        public int Delay
        {
            get { return _delay; }
            set { _delay = value; }
        }

        public int ProduceTime
        {
            get { return _produceTime; }
            set { _produceTime = value; }
        }

        public override Vector2 Position
        {
            get { return _position; }
            set
            {
                _position = value;
                if (_position.X < 0)
                    _position.X = 0;
                if (_position.Y < 0)
                    _position.Y = 0;
                float maxWidth = GameBase.Constant.screenWidth - _idle.LstTexture[0].Width * Scale;
                float maxHeight = GameBase.Constant.screenHeight - _idle.LstTexture[0].Height * Scale;
                if (_position.X > maxWidth)
                    _position.X = maxWidth;
                if (_position.Y > maxHeight)
                    _position.Y = maxHeight;
                _idle.Position = _position;
                _attack.Position = _position;
                _walk.Position = _position;
                _die.Position = _position;
            }
        }

        public HealthPotion Health
        {
            get { return _health; }
            set { _health = value; }
        }

        public int RangeAttack
        {
            get { return _rangeAttack; }
            set { _rangeAttack = value; }
        }

        public int StrengthAttack
        {
            get { return _strengthAttack; }
            set { _strengthAttack = value; }
        }

        public int Defend
        {
            get { return _defend; }
            set { _defend = value; }
        }

        public bool IsRight
        {
            get { return _isRight; }
            set
            {
                _isRight = value;
                _idle.IsRight = _isRight;
                _walk.IsRight = _isRight;
                _attack.IsRight = _isRight;
                _die.IsRight = _isRight;
            }
        }

        public bool IsTargetPerson
        {
            get { return _isTargetPerson; }
            set { _isTargetPerson = value; }
        }

        public bool IsChosen
        {
            get { return _isChosen; }
            set { _isChosen = value; }
        }
        #endregion

        #region Methods
        public Military()
        {
        }
        public Military(string imgStr, Vector2 pos)
        {
            // init military
            init(imgStr, pos);
        }

        public virtual void init(string imgStr, Vector2 pos)
        {
            _imgStr = imgStr;
            _position = pos;

            switch (GameBase.Constant.hardLevel)
            {
                case 0:
                    _AI = new NormalAIBrain(this);
                    break;
                case 1:
                    _AI = new ProfessionalAIBrain(this);
                    break;
            }

            _health = new HealthPotion("Resources/health", -1);
            _rank = new MilitaryRank(@"Resources/rank", 4);

            _state = new NormalState();

            _circleChosen = GameBase.Constant.content.Load<Texture2D>(@"Resources/chosen");
        }

        public void walkTo(Vector2 targetPos)
        {
            if (CurrentState == SOLDIER_STATE.Die)            
                return;
            
            if(CurrentState == SOLDIER_STATE.Idle || CurrentState == SOLDIER_STATE.Attack)
                CurrentState = SOLDIER_STATE.Walk;

            float newWidth = GameBase.Constant.screenWidth - _currentState.LstTexture[0].Width / 2;
            if (targetPos.X > newWidth)
                targetPos.X = newWidth;
            float newHeight = GameBase.Constant.screenHeight - _currentState.LstTexture[0].Height / 2;
            if (targetPos.Y > newHeight)
                targetPos.Y = newHeight;

            Vector2 newPos = new Vector2(targetPos.X - _currentState.LstTexture[0].Width / 2, targetPos.Y - _currentState.LstTexture[0].Height / 2);

            if (newPos.X < 0)
                newPos.X = 0;
            if (newPos.Y < 0)
                newPos.Y = 0;

            TargetPosition = newPos;
        }

        public void attack(Factory factory)
        {
            if (IsAutoControl)
                idle();
            float distance = Vector2.Distance(this.Position, factory.Position);

            if (distance <= RangeAttack)
            {
                //Vector2 posAttack = new Vector2(
                //    factory.Position.X + (float)Constant.random.NextDouble() * 10,
                //    factory.Position.Y + (float)Constant.random.NextDouble() * 10);
                this.Attack(factory.Position);

                if (_attack.isEndOfFrame())
                    factory.beAttacked(this);
            }
            else
            {
                this.walkTo(factory.Position);
            }
        }

        public void attack(Military military)
        {
            _militaryForAttack = military;

            if(IsAutoControl)
                idle();
            float distance = Vector2.Distance(this.Position, military.Position);

            if (distance <= RangeAttack)
            {
                this.Attack(military.Position);
                if (_attack.isEndOfFrame())
                    military.beAttacked(this);
            }
            else
            {
                this.walkTo(military.Position);
            }
        }

        private void beAttacked(Military military)
        {
            float damage = military.StrengthAttack * (float)(100 - Defend) / 100;
            Health.Health -= (int)damage;
            if (CurrentState == SOLDIER_STATE.Idle && IsAutoControl)
            {
                this.attack(military);
            }
            if (Health.Health <= 0)
            {
                CurrentState = SOLDIER_STATE.Die;
                military._kill++;
            }

            // update phisical when state is CrazyState
            if (military.State.getStateName() == @"CrazyState")
            {
                Vector2 direction = military.Position - Position;
                direction.Normalize();
                Vector2 force = GameBase.Constant.force * direction;
                TotalBeForced += force;
            }
        }

        public void Attack(Vector2 targetPos)
        {
            if (CurrentState == SOLDIER_STATE.Die || CurrentState == SOLDIER_STATE.Walk)
                return;

            this.TargetPosition = targetPos;

            CurrentState = SOLDIER_STATE.Attack;
            if(_bullet.isEndOfFrame())
                _bullet.Position = targetPos;
        }

        public bool isDead()
        {
            return (CurrentState == SOLDIER_STATE.Die || Health.Health <= 0);
        }

        public bool isAttacking()
        {
            return CurrentState == SOLDIER_STATE.Attack;
        }

        public void Die()
        {
            CurrentState = SOLDIER_STATE.Die;
        }

        public void idle()
        {
            if (CurrentState == SOLDIER_STATE.Die)
                return;

            CurrentState = SOLDIER_STATE.Idle;
        }

        public override void draw(GameTime gameTime, SpriteBatch spriteBatch,Color color)
        {
            if(CurrentState != SOLDIER_STATE.Die)
                if (_isTargetPerson)
                    spriteBatch.Draw(_circleChosen, new Vector2(Position.X + (Size / 2).X, Position.Y), Color.Red);
                else if(_isChosen)
                    spriteBatch.Draw(_circleChosen, new Vector2(Position.X + (Size / 2).X, Position.Y), Color.Blue);

            _currentState.draw(gameTime, spriteBatch, color * (1 - _currentTotalTime / (GameBase.Constant.timeForDisappear * 1000)));
            _health.draw(gameTime, spriteBatch,color);
            
            if(CurrentState != SOLDIER_STATE.Die) 
                _rank.draw(gameTime, spriteBatch, color);
        }

        public int getMaxHealth() 
        {
            return _health.MaxHealth;
        }

        public virtual Object clone()
        {
            return null;
        }

        public virtual string getName()
        {
            return @"Infantry";
        }

        public override void update(GameTime gameTime)
        {
            if (Position.X < TargetPosition.X)
                IsRight = true;
            if (Position.X > TargetPosition.X)
                IsRight = false;

            _AI.update(gameTime);
            _state.updateState(gameTime, this);

            if (CurrentState == SOLDIER_STATE.Attack && _attack.isEndOfFrame())
            {
                CurrentState = SOLDIER_STATE.Idle;
                _attack.resetFrame();
                _bullet.resetFrame();

                // play gun sound effects.
                int randomIdex = Constant.random.Next() % Constant.listGunSoundEffects.Count;
                SoundEffectInstance soundEffectInstance = Constant.listGunSoundEffects[randomIdex].CreateInstance();
                if(soundEffectInstance.State != SoundState.Playing)
                    Constant.listGunSoundEffects[randomIdex].Play((float)Constant.random.NextDouble() * GameBase.Constant.volume, (float)Constant.random.NextDouble(), (float)Constant.random.NextDouble());
            }

            if (CurrentState == SOLDIER_STATE.Attack)
            {
                _bullet.update(gameTime);
            }

            if (CurrentState == SOLDIER_STATE.Walk)
            {
                if (TargetPosition == Position)
                {
                    CurrentState = SOLDIER_STATE.Idle;
                    return;
                }

                // calculate new position
                Position = calculateNewPosition(Position, TargetPosition, Speed);

                if (_walk.isEndOfFrame())
                {               
                    // play stepping sound effects.
                    int randomIdex = Constant.random.Next() % Constant.listSteppingSoundEffects.Count;
                    SoundEffectInstance soundEffectInstance = Constant.listSteppingSoundEffects[randomIdex].CreateInstance();
                    if (soundEffectInstance.State != SoundState.Playing)
                    {
                        float volume = (float)Constant.random.NextDouble() * GameBase.Constant.volume * 0.5f;
                        Constant.listSteppingSoundEffects[randomIdex].Play(volume, (float)Constant.random.NextDouble(), (float)Constant.random.NextDouble());
                    }
                }
            }
            if (CurrentState == SOLDIER_STATE.Idle && _idle.isEndOfFrame())
            {
                this.Health.regenHealth();
            }
            if (CurrentState == SOLDIER_STATE.Die)
            {
                _currentTotalTime += gameTime.ElapsedGameTime.Milliseconds;
                if (_currentTotalTime > GameBase.Constant.timeForDisappear * 1000)
                    _teamOwner.Infantries.Remove(this);
            }

            _currentState.update(gameTime);

            // calculate new position for force
            setNewPostion();

            // update health
            _health.Position = new Vector2(Position.X, Position.Y + Size.Y * Scale);
            _health.update(gameTime);

            // update rank
            _rank.Position = new Vector2(Position.X + (Size.X * Scale / 2), Position.Y + Size.Y * Scale - _rank.Size.Y);
            _rank.Rank = _kill;

            if ((Health.getRemainHealth() <= 0.25f || _rank.isMaxRank()) && _state.getStateName() != @"CrazyState")                
            {
                _state = new CrazyState();
            }
            if (Health.getRemainHealth() > 0.25f && !_rank.isMaxRank() && _state.getStateName() != @"NormalState")
            {
                _state = new NormalState();
            }
        }

        private Vector2 calculateNewPosition(Vector2 pos, Vector2 targetPos, float speed)
        {
            // A(Position)-------C--------------B(TargetPosition)
            double AB = Math.Sqrt((pos.X - targetPos.X) * (pos.X - targetPos.X) + (pos.Y - targetPos.Y) * (pos.Y - targetPos.Y));
            double AC = speed;
            if (AB < AC)
            {
                return targetPos;
            }
            double k = AC / AB;
            Vector2 vectorAB = targetPos - pos;
            Vector2 X = new Vector2(vectorAB.X * (float)k, vectorAB.Y * (float)k);
            Vector2 C = new Vector2(pos.X + X.X, pos.Y + X.Y);

            return C;
        }

        private void setNewPostion()
        {
            if (_totalBeForced != Vector2.Zero)
            {
                Position -= _totalBeForced;
                Vector2 forceDecreasement = new Vector2(_totalBeForced.X, _totalBeForced.Y);
                forceDecreasement.Normalize();
                forceDecreasement *= GameBase.Constant.decreaseForce;
                if (_totalBeForced.Length() > forceDecreasement.Length())
                    TotalBeForced -= forceDecreasement;
                else
                    _totalBeForced = Vector2.Zero;
            }
        }
        #endregion

        public void respawn(Vector2 targetPos)
        {
            Health.Health = Health.MaxHealth;
            CurrentState = SOLDIER_STATE.Idle;
            Position = targetPos;
            _currentTotalTime = 0;
        }

        public bool isFullHealth()
        {
            return Health.Health == Health.MaxHealth;
        }

        public bool isInRect(Rectangle rect)
        {
            Vector2 pos = new Vector2(Position.X + Size.X / 2, Position.Y + Size.Y / 2);
            if (pos.X >= rect.X && pos.X <= rect.X + rect.Width &&
                pos.Y >= rect.Y && pos.Y <= rect.Y + rect.Height)
                return true;
            return false;
        }

        public bool isInPoint(Vector2 point)
        {
            if (point.X >= Position.X && point.X <= Position.X + Size.X &&
                point.Y >= Position.Y && point.Y <= Position.Y + Size.Y)
                return true;
            return false;
        }
    }
}
